

package com.soso.gae.spider.service.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.cache.Cache;
import javax.cache.CacheException;
import javax.cache.CacheManager;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.google.appengine.api.memcache.InvalidValueException;
import com.google.appengine.api.memcache.MemcacheService;
import com.soso.core.memcache.api.AsyncCacheService;
import com.soso.core.memcache.api.SyncCacheService;
import com.soso.core.service.CacheService;
import com.soso.core.util.other.ArrayUtil;
import com.soso.core.util.other.StrUtil;
import com.soso.gae.spider.memcache.api.impl.AsyncCacheServiceImpl;
import com.soso.gae.spider.memcache.api.impl.SyncCacheServiceImpl;

/**
 * MemcacheService.SetPolicy.SET_ALWAYS：如果不存在具有该键的值则添加值，如果存在具有该键的值则替换现有值；此为默认
 * MemcacheService.SetPolicy.ADD_ONLY_IF_NOT_PRESENT：如果不存在具有该键的值则添加值，如果存在该键则不执行任何操作
 * MemcacheService.SetPolicy.REPLACE_ONLY_IF_PRESENT：如果不存在具有该键的值则不执行任何操作，如果存在具有该键的值则替换现有值
 * 
 * GCacheFactory.EXPIRATION_DELTA：在相对于值的放置时间的给定时间量（以整数秒数表示）后使值过期
 * GCacheFactory.EXPIRATION_DELTA_MILLIS：在相对于值的放置时间的给定时间量（以整数毫秒数表示）后使值过期
 * GCacheFactory.EXPIRATION：使值在给定日期和时间（以 java.util.Date 表示）后过期
 */
public class CacheServiceImpl implements CacheService {

	private static final Log log = LogFactory.getLog(CacheServiceImpl.class);

	private static final long LOCAL_CACHE_TTL = 5000;
	private static final String RESET_DATE_KEY = "cacheResetDate";
	
	private Cache cache;
	private Map<String, Object> localCache;
	private long localCacheTime;
	private int localHits;
	private int cacheHits;

	public CacheServiceImpl() {
		try {
			Map<Object, Object> props = new HashMap<Object, Object>();
			//By default, values stored in memcache are retained as long as possible
	        //props.put(GCacheFactory.EXPIRATION_DELTA, 24*60*60);//将 Cache 实例配置为在值设置后24小时后过期
	        props.put(MemcacheService.SetPolicy.SET_ALWAYS, true);
            cache = CacheManager.getInstance().getCacheFactory().createCache(props);
            
        } catch (CacheException e) {
            log.error("Can't init cache manager. " + e.getMessage());
        }
        localCache = new HashMap<String, Object>();
        localCacheTime = System.currentTimeMillis();
	}
	
	@Override
	public void resetLocalCache() {
		if (System.currentTimeMillis() - localCacheTime > LOCAL_CACHE_TTL) {
			localCache.clear(); 
	        localCacheTime = System.currentTimeMillis();
		}
	}

	@Override
	public void clear() {
		localCache.clear();
		cache.clear();
		put(RESET_DATE_KEY, new Date());
	}

	@Override
	public boolean containsKey(Object arg0) {
		if (localCache.containsKey(arg0)) {
			return true;
		}
		return cache.containsKey(arg0);
	}

	@Override
	public boolean containsValue(Object arg0) {
		log.error("containsValue(Object arg0) not implemented");		
		return false;
	}

	@Override
	public Set entrySet() {
		log.error("entrySet() not implemented");		
		return null;
	}

	@Override
	public Object get(Object key) {
		try {
			if (localCache.containsKey(key)) {
				localHits++;
				return localCache.get(key);
			}
			Object value = cache.get(key);
			localCache.put((String)key, value);
			cacheHits++;
			return value;
		}
		catch (InvalidValueException e) {
			log.error(e.getMessage());
			return null;
		}
	}

	@Override
	public boolean isEmpty() {
		log.error("isEmpty() not implemented");		
		return false;
	}

	@Override
	public Set keySet() {
		log.error("keySet() not implemented");		
		return null;
	}

	@Override
	public Object put(Object key, Object value) {
		localCache.put((String)key, value);
		try {
			return cache.put(key, value);
		}
		catch (Exception e) {
			log.error(e.getMessage());
			return value;
		}
	}

	@Override
	public Object remove(Object key) {
		localCache.remove(key);
		try {
			return cache.remove(key);
		}
		catch (Exception e) {
			log.error(e.getMessage());
			return null;
		}
	}

	@Override
	public int size() {
		log.error("size() not implemented");		
		return 0;
	}

	@Override
	public Collection values() {
		log.error("values() not implemented");		
		return null;
	}

	@Override
	public int getLocalHits() {
		return localHits;
	}

	public int getCacheHits() {
		return cacheHits;
	}

	@Override
	public byte[] getBlob(String key) {
		String chunkList = (String)get(key);
		if (chunkList != null) {
			List<byte[]> data = new ArrayList<byte[]>();
			int size = 0;
			for (String chunkKey : StrUtil.fromCSV(chunkList)) {
				byte[] chunk = (byte[])get(chunkKey);
				if (chunk == null) {
					return null;
				}
				data.add(chunk);
				size += chunk.length;
			}
			return ArrayUtil.packChunks(data);
		}
		return null;
	}

	public static int CACHE_SIZE_LIMIT = 1000000; 

	@Override
	public void putBlob(String key, byte[] data) {
		List<String> chunkList = new ArrayList<String>();
		List<byte[]> chunks = ArrayUtil.makeChunks(data, CACHE_SIZE_LIMIT);
		int i = 0;
		for (byte[] chunk : chunks) {
			String chunkKey = key + String.valueOf(i);
			put(chunkKey, chunk);
			chunkList.add(chunkKey);
			i++;
		}
		put(key, StrUtil.toCSV(chunkList));
	}

	@Override
	public Date getResetDate() {
		return (Date)get(RESET_DATE_KEY);
	}



	@Override
	public AsyncCacheService getAsyncCacheService() {
		AsyncCacheService asyncCacheService = new AsyncCacheServiceImpl();
		return asyncCacheService;
	}

	@Override
	public SyncCacheService getSyncCacheService() {
		SyncCacheService syncCacheService=new SyncCacheServiceImpl();
		return syncCacheService;
	}

	@Override
	public void putAll(Map<? extends Object, ? extends Object> m) {
		// TODO Auto-generated method stub
		
	}

}
